package com.jzqf.wheel;

import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.animation.DecelerateInterpolator;

import androidx.annotation.Nullable;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 轮盘菜单 按Y轴方向登高滑动
 * 条目高度一样
 * 2020/3/31 10:21
 *
 * @author LiuWeiHao
 * Email 872235631@qq.com
 */
public class WheelView extends View {
    private final String TAG = WheelView.class.getName();
    /**
     * 外圆半径 xml设置为dp,在此已转换为px
     */
    private int mWheelRadius;
    /**
     * 内圆半径
     */
    private int mInnerWheelRadius;
    /**
     * 圆心坐标
     */
    private int mCenterX;
    private int mCenterY;
    /**
     * 轮盘条目组件高度
     */
    private int mWheelItemHeight;
    /**
     * 条目分割线 以当前控件高度/条目高度，计算出被选中条目处于与圆心在同一水平线
     * 计算出条目分割线的初始值，滑动时在此范围上下滑动计算坐标
     */
    private List<Rect> mItemDividerLineOldList;
    private List<Rect> mItemImgRectList;
    private List<Point> mItemTxtPointList;
    private int mPaddingLeft = 20;
    private List<Point> mItemPointCenterList;
    /**
     * 条目分割线 滑动后的坐标区域
     */
    private List<Rect> mItemDividerLineList;
    /**
     * 检测按下到抬起时使用的时间
     */
    private long mLastUpdateTime;
    private VelocityTracker mVelocityTracker;
    /**
     * 每秒最大移动角度
     */
    private int mMax_Speed;
    /**
     * 如果移动角度达到该值，则屏蔽点击
     */
    private int mMin_Speed;
    /**
     * 记录上一次的x，y坐标
     */
    private float mLastX;
    private float mLastY;
    /**
     * 按下时的Y坐标
     */
    private float mDownY;
    /**
     * Y轴移动距离
     */
    private float mYMoveDistance = 0;
    /**
     * 数据源
     */
    private List<WheelItem> mDatas = new ArrayList<>();
    /**
     * 条目点击事件
     */
    private OnWheelItemClickListener mOnItemClickListener;
    /**
     * 滚动选择事件
     */
    private OnWheelAngleSelectListener mOnAngleSelectListener;
    private boolean isSetData;
    private Bitmap mBitmapWheel;
    private Bitmap mBitmapDivider;
    private Paint mWeelPaint;
    private Rect mBitmapWheelSrc;
    private Rect mBitmapWheelDst;
    private float mLastAnimateValue = 0;

    public WheelView(Context context) {
        super(context);
    }


    public WheelView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs, 0);
        initAttr(context, attrs, 0);
    }


    public WheelView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttr(context, attrs, defStyleAttr);
    }

    @SuppressLint("NewApi")
    public WheelView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initAttr(context, attrs, defStyleAttr);
    }

    private void initAttr(Context context, AttributeSet attrs, int defStyleAttr) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.WheelView, defStyleAttr, 0);
        mWheelRadius = typedArray.getDimensionPixelSize(R.styleable.WheelView_wheelRadius, 200);
        mInnerWheelRadius = typedArray.getDimensionPixelSize(R.styleable.WheelView_innerWheelRadius, 100);
        mWheelItemHeight = typedArray.getDimensionPixelSize(R.styleable.WheelView_wheelItemHeight, 50);
//        mBitmapWheel = BitmapFactory.decodeResource(getResources(), R.drawable.bg_wheel_left);
//        mBitmapDivider = BitmapFactory.decodeResource(getResources(), R.drawable.img_divider_white_hori);
        typedArray.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int desiredWidth;

        if (widthMode != MeasureSpec.EXACTLY) {
            if (mWheelRadius >= 0) {
                desiredWidth = mWheelRadius;
            } else {
                desiredWidth = widthSize;
            }
        } else {
            desiredWidth = widthSize;
        }
        int desiredHeight;
        if (heightMode != MeasureSpec.EXACTLY) {
            if (mWheelRadius >= 0) {
                desiredHeight = mWheelRadius * 2;
            } else {
                desiredHeight = heightSize;
            }
        } else {
            desiredHeight = heightSize;
        }
        desiredWidth = Math.max(desiredWidth, getSuggestedMinimumWidth());
        desiredHeight = Math.max(desiredHeight, getSuggestedMinimumHeight());
        int width = resolveSizeAndState(desiredWidth, widthMeasureSpec);
        int height = resolveSizeAndState(desiredHeight, heightMeasureSpec);
        setMeasuredDimension(width, height);
    }

    public static int resolveSizeAndState(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        switch (specMode) {
            case MeasureSpec.UNSPECIFIED:
                result = size;
                break;
            case MeasureSpec.AT_MOST:
                result = Math.min(specSize, size);
                break;
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
            default:
                break;
        }
        return result;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        //计算圆环
        int width = right - left;
        int height = bottom - top;
        mCenterX = -width / 2;
        mCenterY = height / 2;

        onLayoutItemDivider();
        onLayoutItemDividerMove((int) mYMoveDistance);
        onLayoutItemImage();
        super.onLayout(changed, left, top, right, bottom);
    }

    /**
     * 计算图片 区域
     */
    private void onLayoutItemImage() {
        if (mItemImgRectList == null) {
            mItemImgRectList = new ArrayList<>();
            mItemTxtPointList = new ArrayList<>();
        } else {
            mItemImgRectList.clear();
            mItemTxtPointList.clear();
        }
        mItemPointCenterList = new ArrayList<>();
        //计算条目区域
        for (int index = 0; index < mItemDividerLineList.size() - 1; index++) {
            Rect rectIndex = mItemDividerLineList.get(index);
            Rect rectNext = mItemDividerLineList.get(index + 1);
            //计算条目区域的中心点
            Point pointImgLeftCenter = new Point();
            pointImgLeftCenter.y = (rectIndex.top + rectNext.bottom) / 2;
            pointImgLeftCenter.x = (int) (mCenterX + Math.sqrt(Math.pow(mInnerWheelRadius + mPaddingLeft, 2) - Math.pow(pointImgLeftCenter.y - mCenterY, 2)));
            mItemPointCenterList.add(pointImgLeftCenter);
            Rect rectImg = new Rect();
            Point pointTxt = new Point();
            pointTxt.y = pointImgLeftCenter.y + 10;
            if (Math.abs(mCenterY - pointImgLeftCenter.y) <= mWheelItemHeight / 2) {
                rectImg.left = pointImgLeftCenter.x;
                rectImg.top = pointImgLeftCenter.y - mWheelItemHeight / 2 + 5;
                rectImg.right = pointImgLeftCenter.x + mWheelItemHeight - 5;
                rectImg.bottom = pointImgLeftCenter.y + mWheelItemHeight / 2 - 5;
            } else {
                rectImg.left = pointImgLeftCenter.x;
                rectImg.top = pointImgLeftCenter.y - mWheelItemHeight / 4;
                rectImg.right = pointImgLeftCenter.x + mWheelItemHeight / 4 * 2;
                rectImg.bottom = pointImgLeftCenter.y + mWheelItemHeight / 4;
                pointTxt.x = rectImg.right + 10;
            }
            mItemImgRectList.add(rectImg);
            mItemTxtPointList.add(pointTxt);
        }
    }


    /**
     * 计算条目分割线区域
     * 分割线始终保持水平，随着手势围绕圆心旋转
     *
     * @param yMoveDistance Y轴移动距离
     */
    private void onLayoutItemDividerMove(int yMoveDistance) {
        if (mItemDividerLineList == null) {
            mItemDividerLineList = new ArrayList<>();
            mItemDividerLineList.addAll(mItemDividerLineOldList);
        }
        for (Rect rect : mItemDividerLineList) {
            rect.top = rect.top + yMoveDistance;
            rect.left = (int) (mCenterX + Math.sqrt(Math.pow(mInnerWheelRadius, 2) - Math.pow(rect.top - mCenterY, 2)));
            rect.bottom = rect.bottom + yMoveDistance;
            rect.right = (int) (mCenterX + Math.sqrt(Math.pow(mWheelRadius, 2) - Math.pow(rect.bottom - mCenterY, 2)));
        }
        //对分割线集合实行由小到大排序
        Collections.sort(mItemDividerLineList, (o1, o2) -> Integer.compare(o1.top, o2.top));
    }

    /**
     * 计算条目分割线区域
     * 分割线始终保持水平，随着手势围绕圆心旋转
     */
    private void onLayoutItemDivider() {
        //计算条目
        //圆心位置上面的第一个线,中间的与圆心在同一水平线，此条目上下各占中心线的一半,逐级向上、向下计算位置
        //只要第一象限和地四象限
        if (mItemDividerLineOldList == null) {
            mItemDividerLineOldList = new ArrayList<>();
        } else {
            //页面大小不变，位置不必重新计算
            return;
        }
        //条目数为奇数,默认选中居中的
        int mid = getMidSelect();
        //线距离水平线的高度从中心线向上，等差数列增加
        int countTop = 0;
        while (countTop < mid) {
            int lineToHorizontalLine = mWheelItemHeight / 2 + (countTop * mWheelItemHeight);
            int centerTopLineStartX = mCenterX + (int) Math.sqrt(Math.pow(mInnerWheelRadius, 2) - Math.pow(lineToHorizontalLine, 2));
            int centerTopLineStartY = mCenterY - lineToHorizontalLine;
            mItemDividerLineOldList.add(new Rect(centerTopLineStartX, centerTopLineStartY,
                    mCenterX + (int) Math.sqrt(Math.pow(mWheelRadius, 2) - Math.pow(lineToHorizontalLine, 2)), centerTopLineStartY));
            countTop++;
        }
        //线距离水平线的高度从中心线向下，等差数列增加
        int countBottom = 0;
        while (countBottom + mid < mDatas.size() + 1) {
            int lineToHorizontalLine = mWheelItemHeight / 2 + (countBottom * mWheelItemHeight);
            int centerBottomLineStartX = mCenterX + (int) Math.sqrt(Math.pow(mInnerWheelRadius, 2) - Math.pow(lineToHorizontalLine, 2));
            int centerBottomLineStartY = mCenterY + lineToHorizontalLine;
            mItemDividerLineOldList.add(new Rect(centerBottomLineStartX, centerBottomLineStartY,
                    (int) (mCenterX + Math.sqrt(mWheelRadius * mWheelRadius - lineToHorizontalLine * lineToHorizontalLine)), centerBottomLineStartY));
            countBottom++;
        }
        //对分割线集合实行由Y坐标小到大排序
        Collections.sort(mItemDividerLineOldList, (o1, o2) -> Integer.compare(o1.top, o2.top));
    }

    /**
     * 获取居中选择的位置
     */
    private int getMidSelect() {
        int mid;
        if (mDatas.size() % 2 == 1) {
            mid = (mDatas.size() + 1);
        } else {
            mid = mDatas.size() / 2;
        }
        return mid;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawCircle(canvas);
        //绘制
        drawDividerLine(canvas);
        drawItems(canvas);
    }

    /**
     * 绘制条目
     */
    private void drawItems(Canvas canvas) {
        Paint paintImg = new Paint();
        paintImg.setAntiAlias(true);
        paintImg.setFilterBitmap(true);
        Paint paintTxt = new Paint();
        paintTxt.setStyle(Paint.Style.FILL);
        paintTxt.setTextSize(18f);
        paintTxt.setColor(Color.WHITE);
        int selectPosition = getSelectPosition();
        for (int index = 0; index < mItemImgRectList.size() && index < mDatas.size(); index++) {
            WheelItem wheelItem = mDatas.get(index);
            Bitmap bitmap = wheelItem.getBitmap();
            //用来截取Bitmap局部所想要显示的像素块区域，通过构造方法中的四个坐标系点确定范围。这个参数可以为null，为null就是整个Bitmap都作为目标资源显示
//            Rect rectSrc = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
            //用来显示的区域，在控件中绘制Bitmap的区域，可以实现拉伸或者缩放
            Rect rectDst = mItemImgRectList.get(index);
            //被选中的条目设置为高亮，其他透明度降低
            if (index == selectPosition) {
                paintImg.setAlpha(255);
                paintTxt.setAlpha(255);
            } else {
                paintImg.setAlpha(100);
                paintTxt.setAlpha(100);
                Point pointTxt = mItemTxtPointList.get(index);
                canvas.drawText(wheelItem.getDesc(), pointTxt.x, pointTxt.y, paintTxt);
            }
            canvas.drawBitmap(bitmap, null, rectDst, paintImg);
        }
    }

    /**
     * 绘制分割线
     */
    private void drawDividerLine(Canvas canvas) {
        Paint paintLine = new Paint(Paint.ANTI_ALIAS_FLAG);
        for (Rect rect : mItemDividerLineList) {
            Rect rectDivider = new Rect(rect.left + 10, rect.top, rect.right - 10, rect.bottom + 1);
            canvas.drawBitmap(mBitmapDivider, null, rectDivider, paintLine);
        }
    }

    private void drawCircle(Canvas canvas) {
//        //外圆 测试位置使用
//        Paint wheelPaint = new Paint();
//        wheelPaint.setColor(Color.parseColor("#ffffff"));
//        wheelPaint.setStyle(Paint.Style.STROKE);
//        wheelPaint.setStrokeWidth(2f);
//        wheelPaint.setAlpha(20);
//        canvas.drawCircle(mCenterX, mCenterY, mWheelRadius, wheelPaint);
//        //内圆
//        canvas.drawCircle(mCenterX, mCenterY, mInnerWheelRadius, wheelPaint);
        //绘制背景图
        if (mBitmapWheel != null) {
            if (mWeelPaint == null) {
                mWeelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            }
            if (mBitmapWheelSrc == null) {
                mBitmapWheelSrc = new Rect(0, 0, mBitmapWheel.getWidth(), mBitmapWheel.getHeight());
            }
            if (mBitmapWheelDst == null) {
                mBitmapWheelDst = new Rect(0, 0, mBitmapWheel.getWidth(), getHeight());
            }
            canvas.drawBitmap(mBitmapWheel, mBitmapWheelSrc, mBitmapWheelDst, mWeelPaint);
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mDownY = event.getY();
                mLastX = event.getX();
                mLastY = event.getY();
                mLastUpdateTime = SystemClock.uptimeMillis();
                if (mVelocityTracker == null) {
                    mVelocityTracker = VelocityTracker.obtain();
                } else {
                    mVelocityTracker.clear();
                }
                mVelocityTracker.addMovement(event);
                break;
            case MotionEvent.ACTION_MOVE:
                mVelocityTracker.addMovement(event);
                mYMoveDistance = event.getY() - mLastY;
                // 滑动在有效范围内
                if (canMove(mYMoveDistance) && event.getX() <= mCenterX + mWheelRadius) {
                    refresh();
                    mLastX = event.getX();
                    mLastY = event.getY();
                }
                break;
            case MotionEvent.ACTION_UP:
                //移动距离<条目高度的一半时，定义为点击事件
                if (mOnItemClickListener != null
                        && Math.abs(event.getY() - mDownY) < mWheelItemHeight / 4) {
                    int clickPositon = getClickPositon(event.getX(), event.getY());
                    if (clickPositon > -1) {
                        mOnItemClickListener.onWheelItemClick(WheelView.this, clickPositon);
                        startAnimation(clickPositon);
                    }
                }
                //移动距离>=条目高度的一半时，定义为滚动事件
                if (mOnAngleSelectListener != null
                        && Math.abs(event.getY() - mDownY) >= mWheelItemHeight / 2) {
                    int position = getSelectPosition();
                    if (position > -1) {
                        mOnAngleSelectListener.onWheelAngleSelect(WheelView.this, position);
                    }
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                if (mVelocityTracker != null) {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
                break;
            default:
                break;
        }
        return true;
    }

    private boolean isInWheel(float x, float y) {
        if (x <= mCenterX - mWheelRadius) {
            int touchrsqr = (int) (x * x + y * y);
            return touchrsqr >= mInnerWheelRadius * mInnerWheelRadius && touchrsqr <= mWheelRadius * mWheelRadius;
        }
        return false;
    }

    private void refresh() {
        isSetData = false;
//        long currentTime = SystemClock.uptimeMillis();
//        long timeDiff = currentTime - mLastUpdateTime;
//        mLastUpdateTime = currentTime;
//        //Y轴滑动的平均速度
//        mVelocityTracker.computeCurrentVelocity(1);
//        float yVelocity = mVelocityTracker.getYVelocity();
//        if (yVelocity > 0.3f) {
//            yVelocity = 0.3f;
//        } else if (yVelocity < 0.3f) {
//            yVelocity = 0.3f;
//        }
        requestLayout();
        invalidate();
    }


    /**
     * 点击后动画延迟
     *
     * @param position 索引
     */
    private void startAnimation(int position) {
        ValueAnimator animator = ValueAnimator.ofFloat(0, mCenterY - mItemPointCenterList.get(position).y);
        animator.setDuration(500);
        animator.addUpdateListener(valueAnimator -> {
            float animatedValue = (float) valueAnimator.getAnimatedValue();
            mYMoveDistance = animatedValue - mLastAnimateValue;
            mLastAnimateValue = animatedValue;
            refresh();
        });
        animator.setInterpolator(new DecelerateInterpolator());
        animator.start();
    }

    private void setSelectPosition(int position) {
        //点击条目自动旋转到居中位置
        mYMoveDistance = mCenterY - mItemPointCenterList.get(position).y;
        Log.i(TAG, "mYMoveDistance=" + mYMoveDistance);
        refresh();
    }

    /**
     * 获取选中状态的位置
     */
    private int getSelectPosition() {
        return getClickPositon(mCenterX + (mInnerWheelRadius + mWheelRadius) / 2, mCenterY);
    }

    /**
     * 根据坐标点判断被选中的条目位置
     *
     * @param x 坐标点X
     * @param y 坐标点Y
     * @return 被选中条目的位置
     */
    private int getClickPositon(float x, float y) {
        int position = -1;
        for (int index = 0; index < mItemDividerLineList.size() - 1; index++) {
            Rect rectIndex = mItemDividerLineList.get(index);
            Rect rectNext = mItemDividerLineList.get(index + 1);
            Rect rect = new Rect(Math.min(rectIndex.left, rectNext.left), rectIndex.top,
                    Math.max(rectIndex.right, rectNext.right), rectNext.bottom);
            if (rect.contains((int) x, (int) y)) {
                position = index;
                break;
            }
        }
        return position;
    }

    /**
     * 移动是否在有效范围内
     *
     * @param yMoveDistance Y轴移动距离
     * @return true 在有效范围内
     */
    private boolean canMove(float yMoveDistance) {
        Rect rectTop = mItemDividerLineList.get(0);
        Rect rectBottom = mItemDividerLineList.get(mItemDividerLineList.size() - 1);
        //向下滑动
        boolean canMove;
        if (yMoveDistance > 0) {
            //最上一个被选中
            canMove = mCenterY - mWheelItemHeight / 2 >= rectTop.top + Math.abs(yMoveDistance);
        } else {
            //底部被选中
            canMove = mCenterY + mWheelItemHeight / 2 <= rectBottom.bottom - Math.abs(yMoveDistance);
        }
        return canMove;
    }


    public void setData(List<WheelItem> list) {
        this.mDatas = list;
        Log.i(TAG, "setData");
        isSetData = true;
    }

    public List<WheelItem> getDatas() {
        return mDatas;
    }

    /**
     * 圆的原点坐标,在屏幕的方向
     */
    enum CenterPosition {
        /**
         * 圆心在左侧
         */
        LEFT,
        /**
         * 圆心在屏幕右侧
         */
        RIGHT
    }

    /**
     * 象限
     */
    enum Quadrant {
        /**
         * 第一象限
         */
        FIRST,
        SECOND,
        THIRD,
        FOURTH
    }

    /**
     * A listener for when the wheel's angle has changed.
     */
    public interface OnWheelAngleSelectListener {
        /**
         * Receive a callback when the wheel's angle has changed.
         */
        void onWheelAngleSelect(WheelView parent, int position);
    }

    public void setOnAngleSelectListener(OnWheelAngleSelectListener onAngleSelectListener) {
        this.mOnAngleSelectListener = onAngleSelectListener;
    }

    public interface OnWheelItemClickListener {
        void onWheelItemClick(WheelView parent, int position);
    }

    public void setOnWheelItemClickListener(OnWheelItemClickListener listener) {
        mOnItemClickListener = listener;
    }

}

